package com.fan.gofast.compent;


import com.fan.gofast.util.DateUtils;
import com.fan.gofast.util.JsonUtil;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Redis缓存配置类
 */
@Component
public class RedisClient {
    public static final Long EXPIRE_TIME_15_MIN = 15 * 60 * 1000L;

    public static final Long EXPIRE_TIME_5_MIN = 5 * 60 * 1000L;
    public static final Long EXPIRE_TIME_23_HOUR = 23 * 60 * 60 * 1000L;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public void setString(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    public List<String> multiGetString(List<String> key) {
        return stringRedisTemplate.opsForValue().multiGet(key);
    }


    public void setString(String key, String value, Long timeout) {
        stringRedisTemplate.opsForValue().set(key, value);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);// 设置过期时间
        }
    }

    public void setString(String key, String value, Long timeout, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, value);
        if (timeout != null) {
            stringRedisTemplate.expire(key, timeout, unit);
        }
    }

    public void remove(String key) {
        stringRedisTemplate.delete(key);
    }

    public void remove(List<String> keys) {
        stringRedisTemplate.delete(keys);
    }

    public void removeByPrefix(String key) {
        stringRedisTemplate.delete(scanMatch(key + "*"));
    }

    /**
     * 存储为string，并设置过期时间
     *
     * @param key        key
     * @param value      value
     * @param expireTime 过期时间
     */
    public void setStringAndExpireAtDate(String key, String value, Date expireTime) {
        stringRedisTemplate.opsForValue().set(key, value);
        setExpireAtDate(key, expireTime);
    }

    /**
     * 存储为string，并设置当天有效
     *
     * @param key   key
     * @param value value
     */
    public void setStringAndExpireAtToday(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
        setExpireAtToday(key);
    }

    public void addListItem(String key, String value) {
        stringRedisTemplate.opsForList().leftPush(key, value);
    }

    public <T> T popListItem(String key, Class<T> c) {
        String s = stringRedisTemplate.opsForList().rightPop(key);
        if (Objects.nonNull(s)) {
            return JsonUtil.convertObjToPojo(s, c);
        }
        return null;
    }

    /**
     * 设置当天有效
     *
     * @param key key
     */
    public void setExpireAtToday(String key) {
        setExpireAtDate(key, DateUtils.startOfDay(LocalDate.now().plusDays(1L)));
    }

    /**
     * 设置某个键的过期时间
     *
     * @param key        key
     * @param expireTime 过期时间
     */
    public void setExpireAtDate(String key, Date expireTime) {
        if (expireTime != null) {
            stringRedisTemplate.expireAt(key, expireTime);// 设置过期时间
        }
    }

    /**
     * 获取集合中某个值的数量
     */
    public long getSetCount(String key) {
        return stringRedisTemplate.opsForSet().size(key);
    }

    /**
     * 向指定key中存放set集合
     *
     * @param key   key
     * @param value value
     */
    public void addToSet(String key, String... value) {
        stringRedisTemplate.opsForSet().add(key, value);
    }

    /**
     * 获取指定的key的set集合
     *
     * @param key key
     * @return Set
     */
    public Set<String> getToSet(String key) {
        return stringRedisTemplate.opsForSet().members(key);
    }

    /***
     * 查询key为hash结构的存储的，
     * @param key
     * @return
     */
    public Set<String> scan(String key) {
        Cursor<Map.Entry<Object, Object>> cursors = stringRedisTemplate.opsForHash().scan(key, ScanOptions.scanOptions().count(1000).build());
        Set<String> strings = new HashSet<>();
        cursors.forEachRemaining(s -> {
            strings.add(s.getKey().toString());
        });
        try {
            cursors.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return strings;
    }

    /***
     * 模糊匹配key
     * @param key
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public List<String> scanMatch(String key) {
        String script = "local dbsize=redis.call('dbsize') local res=redis.call('scan',0,'match',KEYS[1],'count',dbsize) return res[2]";

        DefaultRedisScript<List> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(List.class);

        return stringRedisTemplate.execute(redisScript, Collections.singletonList(key));
    }

    public Object hashGet(String key, String hk) {
        return stringRedisTemplate.opsForHash().get(key, hk);
    }

    /**
     * 获取指定的key的set集合
     *
     * @param key key
     * @return Set
     */
    public void opsForHashPut(String key, String hk, Object obj) {
        stringRedisTemplate.opsForHash().put(key, hk, JsonUtil.toJsonString(obj));
    }

    /**
     * 获取指定的key的set集合
     *
     * @param key key
     * @return Set
     */
    public void opsForHashDel(String key, Object... hk) {
        stringRedisTemplate.opsForHash().delete(key, hk);
    }

    /**
     * 自增加count
     *
     * @param key   key
     */
    public Long increment(String key) {
        return stringRedisTemplate.opsForValue().increment(key);
    }

    /**
     * 自增加count
     *
     * @param key   key
     * @param count count
     */
    public Long increment(String key, int count) {
        return stringRedisTemplate.opsForValue().increment(key, count);
    }

    /**
     * 获取count
     *
     * @param key key
     */
    public int getIncrement(String key) {
        String v = stringRedisTemplate.opsForValue().get(key);
        return v == null ? 0 : Integer.valueOf(v);
    }

    /**
     * 自增加count
     *
     * @param key   key
     * @param count count
     */
    public Long incrementLong(String key, long count) {
        return stringRedisTemplate.opsForValue().increment(key, count);
    }

    public void expire(String key, int i, TimeUnit hours) {
        stringRedisTemplate.expire(key, i, hours);
    }

    /**
     * 返回key对应的ttl，单位：秒
     *
     * @param  key
     * @return
     * @author leijian
     * @date 2020/11/24 11:25
     */
    public long getExpire(String key) {
        return stringRedisTemplate.getExpire(key);
    }

    public boolean hasKey(String key) {
        return stringRedisTemplate.hasKey(key);
    }

    public Long incrementAndExpireAtToday(String key, int count) {
        Long val = stringRedisTemplate.opsForValue().increment(key, count);
        setExpireAtToday(key);
        return val;
    }

    /**
     * 获取对象类型
     *
     * @param key   key
     * @param clazz class
     * @return class
     */
    public <T> T getToObject(String key, Class<T> clazz) {
        String value = stringRedisTemplate.opsForValue().get(key);
        if (value != null) {
            return JsonUtil.fromJson(value, clazz);
        }
        return null;
    }

    /**
     * 获取对象类型
     *
     * @param key   key
     * @param clazz class
     * @return class
     */
    public <T> List<T> getToList(String key, Class<T> clazz) {
        String value = stringRedisTemplate.opsForValue().get(key);
        if (value != null) {
            return JsonUtil.wapperStringToList(value, clazz);
        }
        return null;
    }

    /**
     * @param key   key
     * @param value value
     * @return java.lang.Boolean
     * @author leijian
     * @date 2020/8/12 012 16:11
     */
    public Boolean setNX(String key, String value) {
        return stringRedisTemplate.opsForValue().setIfAbsent(key, value);
    }
}